import {
  Component,
  OnInit,
  Input,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  forwardRef,
  OnChanges,
  SimpleChanges,
  Output,
  EventEmitter,
} from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { Observable, BehaviorSubject } from 'rxjs'
import { debounceTime, switchMap, map } from 'rxjs/operators'
import { toBoolean } from 'ng-zorro-antd/core'

import { AamsSelectService } from './aams-select.service'
import { IAamsLevel, IAamsItem, IAamsQuery } from './aams-select.interface'
import { IBiz } from '@shared/interfaces/biz'

@Component({
  selector: 'app-aams-select',
  templateUrl: './aams-select.component.html',
  styles: [],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    AamsSelectService,
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => AamsSelectComponent),
      multi: true,
    },
  ],
})
export class AamsSelectComponent implements ControlValueAccessor, OnInit, OnChanges {
  constructor(private service: AamsSelectService, private cdr: ChangeDetectorRef) {}

  private _disabled = false

  // 选中的项
  _value: IAamsItem

  @Input() level: IAamsLevel
  @Input() placeholder: string
  @Input() insCode: string
  @Input() agentCode: string
  @Input() intMerCode: string

  @Output() valueChange = new EventEmitter()

  // 选项列表
  optionList = []

  private pageNumber = 1
  private _options: IAamsItem[] = []

  // 请求参数
  params: IAamsQuery = {
    page: 1,
    size: 10,
  }

  searchChange$ = new BehaviorSubject(this.params)
  optionList$: Observable<IAamsItem[]>
  isLoading = false
  onChange: (value: IAamsItem) => void = () => null
  onTouched: () => void = () => null
  compareFn = (o1: IAamsItem, o2: IAamsItem) => (!!o1 && !!o2 && o1.code === o2.code)

  @Input()
  set disabled(value: boolean) {
    this._disabled = toBoolean(value)
  }

  get disabled(): boolean {
    return this._disabled
  }

  set value(value: IAamsItem) {
    this._value = value
    this.optionList = value ? [value, ...this.optionList] : []
    this.onChange(this._value)
    if (!!value) {
      this.valueChange.next({level: this.level, value})
    }
  }

  get value(): IAamsItem {
    return this._value
  }

  clearValue(): void {
    this._value = null
    this.optionList = []
  }

  writeValue(value: IAamsItem): void {
    if (value) {
      this._options = [value, ...this._options]
    }
    // TODO: 判断是否在 options中
    this.value = value
    this.cdr.markForCheck()
  }

  registerOnChange(fn: (value: IAamsItem) => void): void {
    this.onChange = fn
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn
  }

  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled
    this.cdr.markForCheck()
  }

  ngOnInit() {
    // 搜索变化，有节流功能
    this.optionList$ = this.searchChange$.asObservable().pipe(
      debounceTime(500),
      switchMap(params => this.service.query(params)),
      map(response => {
        this.pageNumber = response.page
        const allOptions = [...this._options, ...response.data]
        const uniqueCodes = Array.from(new Set(allOptions.map(o => o.code)))
        this._options = uniqueCodes.map(code => allOptions.find(o => o.code === code))
        this.isLoading = false
        return this._options
      }),
    )

    this.params = this.getParams(this.level)
    this.searchChange$.next(this.params)
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.insCode || changes.agentCode || changes.intMerCode) {
      const filter: IBiz = {}
      if (changes.insCode) {
        filter.insCode = changes.insCode.currentValue
      }
      if (changes.agentCode) {
        filter.agentCode = changes.agentCode.currentValue
      }
      if (changes.intMerCode) {
        filter.intMerCode = changes.intMerCode.currentValue
      }

      this.params = this.getParams(this.level, filter)
      this.onSearch('')
    }
  }

  loadMore(): void {
    if (this.isLoading) {
      return
    }
    this.isLoading = true
    this.params.page = this.pageNumber + 1
    this.searchChange$.next(this.params)
  }

  onSearch(value: string): void {
    this.isLoading = true
    this.pageNumber = 1
    this._options = []
    this.params.searchTerm = value
    this.params.page = 1
    this.searchChange$.next(this.params)
  }

  modelChange(model: IAamsItem): void {
    // 选中的值被清除的时候需要重新拉取下拉列表
    if (model === null && !this.optionList.length) {
      this.params.searchTerm = ''
      this.searchChange$.next(this.params)
    }
  }

  getParams(level: IAamsLevel, filter?: IBiz): IAamsQuery {
    const params: IAamsQuery = Object.assign({}, this.params)
    params.level = level
    if (filter) {
      Object.assign(params, filter)
    }

    return params
  }
}
